/* Include files. */ 
extern "C" { 
	#include <standard.h>
	#include "serialize.h"
	#include "debug.h"
	#include "util.h"
}

#include "taskcore.h"
#include "subsystemcore.h"
#include "message.h"
#include "netobject.h"

/* Local function declarations. */

/* Macro API definitions. */

/* Global variable declarations. */

/* Type definitions. */

/* Local function definitions. */

#ifdef U_LOOP
extern struct uloop_timeout script_uloop_handle;
#else
extern struct ev_loop *main_loop;
#endif
extern void setEnvs(lua_State *L);

static int loadFile(lua_State *L) 
{
	luaL_openlibs(L);
	setEnvs(L);
	const char *filename = (const char *)lua_touserdata(L, 1);
	int err = luaL_loadfile(L, filename);
	if (err != LUA_OK){
		const char *error = lua_tostring(L, -1);
		ERROR("load file error, %s %s!\n", filename, error);
		return 0;
	}
	
	return 1;
}

int luaExit(lua_State *L) 
{
	const char *scriptname = luaL_checkstring(L, -1);
	subSystem* sub = subSystem::findSubSystem(scriptname);
	delete sub;
	
	return 0;
}	

#if 1
static void * taskMemStatus(void * ud, void *ptr, 
		size_t out, size_t in)
{
	subSystem *sub = (subSystem *)ud;
	sub->_cost_mem += in;
	if (ptr) {
		sub->_cost_mem -= out;
	}
	if (in == 0) {
		free(ptr);
		return NULL;
	} else {
		return realloc(ptr, in);
	}
}
#endif

int luaDestroy(lua_State *L) 
{
#ifdef U_LOOP
#else
	ev_break(main_loop, EVBREAK_ALL);
#endif
	return 0;
}

int luaNewSub(lua_State *L) 
{
	void * args;
	int top;
	int err;
	
	const char *name = luaL_checkstring(L, 1);
	const char *filename = luaL_checkstring(L, 2);

	if (subSystem::findSubSystem(name)) {
		lua_pushboolean(L, false);
		return 1;
	}
	
	subSystem *sub = new subSystem(name, NULL);
	lua_State *sub_L = lua_newstate(taskMemStatus, sub);
	//lua_State *sub_L = luaL_newstate();
	if (sub_L == NULL) {
		luaL_error(L, "luaL_newstate failed");
		ERROR("luaL_newstate failed");
		goto error;
	}	
	lua_gc(sub_L, LUA_GCSTOP, 0);
	sub->_L = sub_L;
	lua_pushcfunction(sub_L, loadFile);
	lua_pushlightuserdata(sub_L, (void *)filename);
	err = lua_pcall(sub_L, 1, 1, 0);
	if (err != LUA_OK) {
		size_t sz = 0;
		const char * error = lua_tolstring(sub_L, -1, &sz);
		lua_pushlstring(L, error, sz);
		lua_error(L);
		goto error;
	}
	lua_pushcfunction(L, luaSeriPack);
	lua_insert(L, 3);
	top = lua_gettop(L);
	lua_call(L, top-3, 1);
	args = lua_touserdata(L, -1);
	lua_pushcfunction(sub_L, luaSeriUnpack);
	lua_pushlightuserdata(sub_L, args);
	err = lua_pcall(sub_L, 1, LUA_MULTRET, 1);
	if (err != LUA_OK) {
		size_t sz = 0;
		const char * error = lua_tolstring(sub_L, -1, &sz);
		lua_pushlstring(L, error, sz);
		lua_error(L);
		goto error;
	}
	lua_pushlightuserdata(sub_L, sub);
	lua_setfield(sub_L, LUA_REGISTRYINDEX, "sub");
	lua_pushcfunction(sub_L, traceback);
	lua_insert(sub_L, 1);
	top = lua_gettop(sub_L);
	err = lua_pcall(sub_L, top - 2, 0, 1); 
	if (err != LUA_OK) {
		size_t sz = 0;
		const char * error = lua_tolstring(sub_L, -1, &sz);
		lua_pushlstring(L, error, sz);
		lua_error(L);
		goto error;
	}
	lua_settop(sub_L, 0);
	lua_gc(sub_L, LUA_GCRESTART, 0);
	lua_pushboolean(L, true);
	INFO("subsystem %s create success!\n", name);
	return 1;
	
error:
	delete sub;
	return -1;
}

int dispatch(subSystem *sub, const char *from, const char *to, 
				int session, const char *type, const char* data) 
{
	int ret;
	lua_State *L = sub->_L;
	
	lua_pushcfunction(L, traceback);
	lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)dispatch);
	
	lua_pushstring(L, from);
	lua_pushstring(L, to);
	lua_pushinteger(L, session);
	lua_pushstring(L, type);
	lua_pushstring(L, data);
	sub->_dispatch_count++;

	ret = lua_pcall(L, 5, LUA_MULTRET, 1);
	if (ret == LUA_OK) {
		lua_settop(L, 0);
		if (sub->_dispatch_count % 10 == 0) {
			lua_gc(sub->_L, LUA_GCCOLLECT, 0);
		}
		return 0;
	}
	else {
		size_t sz = 0;
		const char * error = lua_tolstring(L, -1, &sz);
		ERROR("%s\n", error);
		lua_close(L);
		return -1;	
	}
}

int luaSend(lua_State *L) 
{
	lua_getfield(L, LUA_REGISTRYINDEX, "sub");
	subSystem *sub = (subSystem *)lua_touserdata(L, -1);
	if (sub == NULL) {
		return luaL_error(L, "subSystem error");
	}
	return sub->sendSubSystem(L);
}

int luaMem(lua_State *L) 
{
	lua_getfield(L, LUA_REGISTRYINDEX, "sub");
	subSystem *sub = (subSystem *)lua_touserdata(L, -1);
	if (sub == NULL) {
		return luaL_error(L, "subSystem error");
	}
	lua_pushinteger(L, sub->_cost_mem);
	return 1;
}

int luaCpu(lua_State *L) 
{
	lua_getfield(L, LUA_REGISTRYINDEX, "sub");
	subSystem *sub = (subSystem *)lua_touserdata(L, -1);
	if (sub == NULL) {
		return luaL_error(L, "subSystem error");
	}
	lua_pushinteger(L, sub->_cost_cpu);
	sub->_cost_cpu = 0;
	return 1;
}

int luaName(lua_State *L) 
{
	lua_getfield(L, LUA_REGISTRYINDEX, "sub");
	subSystem *sub = (subSystem *)lua_touserdata(L, -1);
	if (sub == NULL) {
		return luaL_error(L, "subSystem error");
	}
	lua_pushstring(L, sub->_name.c_str());
	return 1;
}

int luaNetSend(lua_State *L) 
{
	lua_getfield(L, LUA_REGISTRYINDEX, "sub");
	subSystem *sub = (subSystem *)lua_touserdata(L, -1);
	if (sub == NULL) {
		return luaL_error(L, "subSystem error");
	}
	return sub->sendNetObject(L);
}

int luaCallback(lua_State *L) 
{
	luaL_checktype(L, 1, LUA_TFUNCTION);
	lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)dispatch);
	return 0;
}


